﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using LevelEditor.Dao;
using LevelEditor.Objects;
using LevelEditor.Objects.Collisions;
using LevelEditor.App;

namespace LevelEditor
{
    public partial class EditCollisions : Form
    {
        private enum State
        {
            DrawingRect, View, Dragging, SelectingBehavior,
        }

        private Timer timer;
        private Image edittingImage;
        private Tile edittingTile;
        private Behavior selectedBehavior;
        private State state;
        private bool startedDrawing;

        private CollisionsData collisionDataBigSize;

        private CollisionsData collisionData;
        public CollisionsData CollisionData
        {
            get { return collisionData; }
        }

        private Rectangle currentDrawingRect;

        private int editingRectIndex = -1;

        private Point lastMousePosition;

        private int imageIndex; // the index in the behavior's image list

        private bool isModifyingCollision;

        public EditCollisions(Tile tile)
        {
            InitializeComponent();

            this.state = State.SelectingBehavior;
            this.edittingTile = tile;
            
            LoadBehaviors(tile);

        }

        private void Initialize(Image img, int imageIndex)
        {
            this.collisionDataBigSize = new CollisionsData();

            this.collisionData = new CollisionsData();
            this.edittingImage = img;
            this.state = State.View;
            this.imageIndex = imageIndex;

            if (img == null)
            {
                ErrorController.ShowOkError(ErrorController.LevelEditorError.APP_NO_IMAGE_SELECTED);
                return;
            }

            this.isModifyingCollision = false;

            timer = new Timer();
            timer.Interval = 1;

            timer.Enabled = true;
            timer.Start();
            timer.Tick += timer_Tick;

            tileImage.Image = img;
            tileImage.SizeMode = PictureBoxSizeMode.StretchImage;

            imageDisplayer.MouseDown += EditCollisions_MouseDown;
            imageDisplayer.MouseMove += EditCollisions_MouseMove;
            imageDisplayer.MouseUp += EditCollisions_MouseUp;
            imageDisplayer.BackColor = Color.Transparent;
            this.imageDisplayer.BringToFront();

            if (selectedBehavior.CollisionMap.Count != 0)
            {
                AlterCollisions(img);                
            }
        }

        private void LoadBehaviors(Tile tile)
        {
            List<Trigger> MyTrigger = tile.MyTrigger;

            behaviorSelector.Items.Clear();

            for (int k = 0; k < MyTrigger.Count; k++)
            {
                behaviorSelector.Items.Add(MyTrigger[k].MappedBehavior.Name);
            }

            for (int k = 0; k < tile.standAloneBehaviors.Count; k++)
            {
                behaviorSelector.Items.Add(tile.standAloneBehaviors[k].Name);
            }
        }

        void EditCollisions_MouseUp(object sender, MouseEventArgs e)
        {
            if (this.state == State.Dragging)
            {
                this.state = State.View;
                collisionDataBigSize.Collisions.RemoveAt(editingRectIndex);
                collisionDataBigSize.Collisions.Insert(editingRectIndex, currentDrawingRect);
                return;
            }
            if (!startedDrawing) return;

            collisionDataBigSize.Collisions.Add(currentDrawingRect);
            startedDrawing = false;
        }

        void EditCollisions_MouseMove(object sender, MouseEventArgs e)
        {
            Point localPosition = PointToClient(Cursor.Position);

            if (this.state == State.Dragging)
            {
                localPosition = this.imageDisplayer.PointToClient(e.Location);
                Point p = new Point();
                p.X = localPosition.X + currentDrawingRect.Left;
                p.Y = localPosition.Y + currentDrawingRect.Top;
                currentDrawingRect.Location = new Point( Cursor.Position.X,  Cursor.Position.Y);
                
                return;
            }

            if (!startedDrawing) return;
            
            currentDrawingRect.Size = new Size(Math.Abs( currentDrawingRect.Location.X - localPosition.X), Math.Abs( currentDrawingRect.Y - localPosition.Y));
        }

        void EditCollisions_MouseDown(object sender, MouseEventArgs e)
        {
            Point localPosition = this.PointToClient(Cursor.Position);
            
            if (this.state == State.View)
            {
                for (int k = 0; k < collisionDataBigSize.Collisions.Count; k++)
                {
                    if (collisionDataBigSize.Collisions[k].Contains(localPosition))
                    {
                        this.state = State.Dragging;
                        lastMousePosition = this.imageDisplayer.PointToClient(e.Location);
                        editingRectIndex = k;
                        currentDrawingRect = collisionDataBigSize.Collisions[k];
                        break;
                    }
                }
                
                return;
            }

            if (startedDrawing) return;

            startedDrawing = true;
            currentDrawingRect = new Rectangle(localPosition, Size.Empty);
            this.behaviorSelector.Enabled = false;
        }

        void timer_Tick(object sender, EventArgs e)
        {
            DrawCollisionData();
        }


        private void DrawCollisionData()
        {
            Graphics g = this.tileImage.CreateGraphics();
            this.tileImage.Image = edittingImage;

            if (startedDrawing || this.state == State.Dragging)
            {
                g.DrawRectangle(new Pen(new SolidBrush(Color.Black), 1), currentDrawingRect);
            }

            for (int k = 0; k < collisionDataBigSize.Collisions.Count; k++)
            {
                g.FillRectangle(new SolidBrush(Color.FromArgb(100, Color.PaleGreen)), collisionDataBigSize.Collisions[k]);
            }

            g.Dispose();
        }

        private void drawRectBtn_CheckedChanged(object sender, EventArgs e)
        {
            if (this.state == State.DrawingRect)
            {
                this.state = State.View;
                return;
            }

            this.imageDisplayer.Location = tileImage.Location;
            this.imageDisplayer.Size = tileImage.Size;

            this.state = State.DrawingRect;
        }

        private void AlterCollisions(Image image)
        {
            edittingImage = image;

            CollisionsData data = selectedBehavior.CollisionMap.Find(delegate(CollisionsData c)
            {
                return c.ImageName == selectedBehavior.Name + imageIndex;
            });

            if (data == null)
                return;

            this.isModifyingCollision = true;

            float aspectX = (this.tileImage.Width / (float)edittingImage.Width);
            float aspectY = (this.tileImage.Height / (float)edittingImage.Height);

            for (int k = 0; k < data.Collisions.Count; k++)
            {
                Rectangle r = data.Collisions[k];
                r.Width = (int)(r.Width * aspectX);
                r.Height = (int)(r.Height * aspectY);
                r.Location = new Point((int)(r.Location.X * aspectX), (int)(r.Location.Y * aspectY));
                data.Collisions.RemoveAt(k);
                data.Collisions.Insert(k, r);
            }
            collisionDataBigSize = new CollisionsData();
            collisionDataBigSize.Collisions.AddRange(data.Collisions);
        }

        private void finishBtn_Click(object sender, EventArgs e)
        {
            if (collisionDataBigSize.Collisions.Count <= 0)
            {
                ErrorController.ShowOkError(ErrorController.LevelEditorError.APP_NO_COLLISION_DATA);
                return;
            }

            float aspectRatioX = edittingImage.Width / (float)imageDisplayer.Width;
            float aspectRatioY = edittingImage.Height / (float)imageDisplayer.Height;
            for (int k = 0; k < collisionDataBigSize.Collisions.Count; k++)
            {
                Rectangle rect = collisionDataBigSize.Collisions[k];
                rect.Location = new Point((int)(rect.Location.X * aspectRatioX), (int)(rect.Location.Y * aspectRatioY));
                rect.Size = new Size((int)(rect.Size.Width * aspectRatioX), (int)(rect.Size.Height * aspectRatioY));

                collisionData.Collisions.Add(rect);
            }

            collisionData.ImageName = selectedBehavior.Name + imageIndex;

            Trigger trigger = Tile.CurrentSelectedTile.MyTrigger.Find(delegate(Trigger t)
            {
                return t.MappedBehavior.Name == selectedBehavior.Name;
            });

            if (trigger != null)
            {
                if (trigger.MappedBehavior.CollisionMap.Count != 0)
                {
                    CollisionsData d = trigger.MappedBehavior.CollisionMap.Find(delegate(CollisionsData data)
                    {
                        return collisionData.ImageName == data.ImageName;
                    });
                    if (d != null)
                    {
                        d.Collisions.Clear();
                        d.Collisions.AddRange(collisionData.Collisions);
                    }
                }
                else trigger.MappedBehavior.CollisionMap.Add(collisionData);
            }
            else
            {
                Behavior standAlone = Tile.CurrentSelectedTile.standAloneBehaviors.Find(delegate(Behavior b)
                {
                    return b.Name == selectedBehavior.Name;
                });

                if (isModifyingCollision)
                {
                    CollisionsData d = standAlone.CollisionMap.Find(delegate(CollisionsData data)
                    {
                        return data.ImageName == collisionData.ImageName;
                    });
                    d.Collisions.Clear();
                    d.Collisions.AddRange(collisionData.Collisions);
                }
                else standAlone.CollisionMap.Add(collisionData);

            }

            this.behaviorSelector.Enabled = true;

            MessageBox.Show("Collision Mapped!");
        }

        private void behaviorSelector_SelectedIndexChanged(object sender, EventArgs e)
        {
            Behavior b = null;
            Trigger trigger = Tile.CurrentSelectedTile.MyTrigger.Find(delegate(Trigger t)
            {
                return t.MappedBehavior.Name == behaviorSelector.Items[behaviorSelector.SelectedIndex].ToString();
            });

            if (trigger != null)
                b = trigger.MappedBehavior;
            else
                b = Tile.CurrentSelectedTile.standAloneBehaviors.Find(delegate(Behavior sb)
                {
                    return sb.Name == behaviorSelector.Items[behaviorSelector.SelectedIndex].ToString();
                });

            if (b == null)
            {

                ErrorController.ShowOkError(ErrorController.LevelEditorError.APP_NO_SELECTED_BEHAVIOR);
                return;
            }

            if (b.Images.Count <= 0)
            {
                ErrorController.ShowOkError(ErrorController.LevelEditorError.APP_BEHAVIOR_NO_IMAGES);
                return;
            }

            selectedBehavior = b;

            if (b.IsSpriteSheet())
            {
                Initialize(b.SpriteSheet, 0);
            }
            else
            {
                LoadImageSequence viewImageSequence = new LoadImageSequence(b.Images, true);
                if (viewImageSequence.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    Initialize(viewImageSequence.SelectedImage, viewImageSequence.IndexInBehaviorList);
                }
                
            }
        }

        
    }
}
